<template>
  <div class="app-layout">
    <div class="sidebar">
      <div class="logo-section">
        <img src="@/assets/logo.png" alt="知识助手" width="120" height="120" />
        <span class="logo-text">知识助手</span>
      </div>
      <el-button class="new-chat-button" @click="newChat">
        <i class="fa-solid fa-plus"></i>
        &nbsp;新会话
      </el-button>
    </div>
    <div class="main-content">
      <div class="chat-header">
        <h2>小赞智识库</h2>
        <span class="chat-description">基于企业知识库的智能问答系统</span>
      </div>
      <div class="chat-container">
        <div class="message-list" ref="messageListRef">
          <div
            v-for="(message, index) in messages"
            :key="index"
            :class="
              message.isUser ? 'message user-message' : 'message bot-message'
            "
          >
            <!-- 会话图标 -->
            <div
              :class="
                message.isUser
                  ? 'fa-solid fa-user message-icon'
                  : 'fa-solid fa-database message-icon'
              "
            ></div>
            <!-- 会话内容 -->
            <div class="message-content">
              <div :class="['message-body', message.isUser?'message-user':'']">
                <!-- 用户消息使用简单格式 -->
                <template v-if="message.isUser">
                  <div class="plain-text" v-html="formatUserContent(message.content)"></div>
                </template>
                <!-- 机器人消息使用 Markdown 渲染 -->
                <template v-else>
                  <div
                    v-if="message.content"
                    v-html="renderMarkdown(message.displayContent || message.content)"
                    class="markdown-content"
                  ></div>
                </template>

                <!-- loading 动画 -->
                <span
                  class="loading-dots"
                  v-if="message.isThinking || message.isTyping"
                >
                  <span class="dot"></span>
                  <span class="dot"></span>
                  <span class="dot"></span>
                </span>
              </div>
            </div>
          </div>
        </div>
        <div class="input-container">
          <el-input
            v-model="inputMessage"
            placeholder="请输入消息"
            @keyup.enter="sendMessage"
            :disabled="isSending"
          ></el-input>
          <el-button @click="sendMessage" :disabled="isSending" type="primary"
            >发送</el-button
          >
        </div>
      </div>
    </div>

    <!-- 嵌入的聊天窗口 -->
    <iframe
      src="https://ragflow.itxiaozan.com/next-chats/widget?shared_id=d7e6ba10af2511f086add2f697588d07&from=chat&auth=UxYmUyYzIwYWU3MjExZjBiNWI0N2EzOW&streaming=true&mode=master&streaming=true"
      style="position:fixed;bottom:0;right:0;width:100px;height:100px;border:none;background:transparent;z-index:9999"
      frameborder="0"
      allow="microphone;camera"
      id="chat-widget-button"
    ></iframe>
  </div>
</template>

<script setup>
import { onMounted, ref, watch, nextTick } from 'vue'
import axios from 'axios'
import { v4 as uuidv4 } from 'uuid'
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

// 初始化 markdown-it 带代码高亮
const md = new MarkdownIt({
  html: true,
  linkify: true,
  typographer: true,
  breaks: true,
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return '<pre class="hljs"><code>' +
               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
               '</code></pre>';
      } catch (__) {}
    }

    return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
  }
})

const messageListRef = ref()
const isSending = ref(false)
const uuid = ref()
const inputMessage = ref('')
const messages = ref([])

// 流式渲染相关
const streamingRenderInterval = ref(null)
const currentStreamingMessage = ref(null)
const hasReceivedMetadata = ref(false)
const latestContent = ref('')

// 初始化
onMounted(() => {
  createNewSession()
  watch(messages, () => scrollToBottom(), { deep: true, flush: 'post' })
  setupChatWidget()
})

// 设置聊天窗口组件
const setupChatWidget = () => {
  window.addEventListener('message', e => {
    if(e.origin !== 'https://ragflow.itxiaozan.com') return;

    if(e.data.type === 'CREATE_CHAT_WINDOW'){
      if(document.getElementById('chat-win')) return;
      const iframe = document.createElement('iframe');
      iframe.id = 'chat-win';
      iframe.src = e.data.src;
      iframe.style.cssText = 'position:fixed;bottom:104px;right:24px;width:380px;height:500px;border:none;background:transparent;z-index:9998;display:none';
      iframe.frameBorder = '0';
      iframe.allow = 'microphone;camera';
      document.body.appendChild(iframe);
    } else if(e.data.type === 'TOGGLE_CHAT'){
      const chatWindow = document.getElementById('chat-win');
      if(chatWindow) chatWindow.style.display = e.data.isOpen ? 'block' : 'none';
    } else if(e.data.type === 'SCROLL_PASSTHROUGH') {
      window.scrollBy(0, e.data.deltaY);
    }
  });
}

// 滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    if (messageListRef.value) {
      messageListRef.value.scrollTop = messageListRef.value.scrollHeight
    }
  })
}

// 欢迎消息
const hello = () => {
  const welcomeMsg = {
    isUser: false,
    content: '你好，我是企业知识库管理系统，你可以向我提问来获取知识库中的相关信息',
    displayContent: '你好，我是企业知识库管理系统，你可以向我提问来获取知识库中的相关信息',
    isTyping: false,
    isThinking: false
  }
  messages.value.push(welcomeMsg)
}

// 渲染 Markdown
const renderMarkdown = (content) => {
  if (!content) return ''
  return md.render(content)
}

// 格式化用户内容（简单的 HTML 转义和换行）
const formatUserContent = (content) => {
  if (!content) return ''

  return content
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;")
    .replace(/\n/g, '<br>')
}

// 发送消息
const sendMessage = () => {
  if (inputMessage.value.trim() && !isSending.value) {
    sendRequest(inputMessage.value.trim())
    inputMessage.value = ''
  }
}

// 发送请求
const sendRequest = (message) => {
  isSending.value = true

  // 添加用户消息
  const userMsg = {
    isUser: true,
    content: message,
    isTyping: false,
    isThinking: false
  }
  messages.value.push(userMsg)

  // 添加机器人加载消息
  const botMsg = {
    isUser: false,
    content: '',
    displayContent: '', // 用于显示的内容
    isTyping: true,
    isThinking: false
  }
  messages.value.push(botMsg)
  currentStreamingMessage.value = botMsg

  scrollToBottom()

  // 重置流式渲染状态
  hasReceivedMetadata.value = false
  latestContent.value = ''

  // 使用流式请求
  sendStreamRequest(message, botMsg)
}

// 开始流式渲染（每3秒渲染一次最新内容）
const startStreamingRender = (botMsg) => {
  // 清除之前的定时器
  if (streamingRenderInterval.value) {
    clearInterval(streamingRenderInterval.value)
    streamingRenderInterval.value = null
  }

  // 设置渲染定时器，每3秒渲染一次最新内容
  streamingRenderInterval.value = setInterval(() => {
    if (botMsg && latestContent.value && botMsg.displayContent !== latestContent.value) {
      // 渲染最新的内容
      botMsg.displayContent = latestContent.value
      botMsg.content = latestContent.value
      scrollToBottom()
      console.log('定时渲染更新:', latestContent.value.length, '字符')
    }
  }, 3000) // 每3秒更新一次
}

// 停止流式渲染
const stopStreamingRender = () => {
  if (streamingRenderInterval.value) {
    clearInterval(streamingRenderInterval.value)
    streamingRenderInterval.value = null
    console.log('停止流式渲染')
  }
}

// 最终渲染
const renderFinalContent = (botMsg) => {
  if (botMsg && latestContent.value) {
    botMsg.displayContent = latestContent.value
    botMsg.content = latestContent.value
    botMsg.isTyping = false
    scrollToBottom()
    console.log('最终渲染:', latestContent.value.length, '字符')
  }
}

// 流式响应请求
const sendStreamRequest = async (message, botMsg) => {
  const url = `https://api.itxiaozan.com/api/chat/stream?question=${encodeURIComponent(message)}&session_id=${uuid.value}`

  console.log('发送流式请求:', {
    url,
    session_id: uuid.value,
    question: message
  })

  try {
    const response = await fetch(url)

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''

    while (true) {
      const { done, value } = await reader.read()

      if (done) {
        botMsg.isTyping = false
        isSending.value = false
        // 确保所有内容都已显示
        renderFinalContent(botMsg)
        stopStreamingRender()
        scrollToBottom()
        break
      }

      // 解码数据块
      const chunk = decoder.decode(value, { stream: true })
      buffer += chunk

      // 处理缓冲区中的完整行
      const lines = buffer.split('\n')
      buffer = lines.pop() || '' // 保留不完整的行

      for (const line of lines) {
        if (line.trim() === '') continue

        // 检查是否是 data: 前缀的格式
        if (line.startsWith('data: ')) {
          const jsonStr = line.substring(6) // 移除 'data: ' 前缀
          try {
            const data = JSON.parse(jsonStr)
            console.log('收到数据:', data)

            if (data.type === 'metadata') {
              console.log('收到元数据:', data.data)
              hasReceivedMetadata.value = true
              // 检测到metadata后开始定时渲染
              startStreamingRender(botMsg)
            }
            else if (data.type === 'content') {
              // 直接使用当前content的内容，不累积
              latestContent.value = data.data
              console.log('最新内容长度:', latestContent.value.length)

              // 注意：这里不立即渲染，等待定时器渲染
            }
            else if (data.type === 'end') {
              console.log('收到结束信号')
              botMsg.isTyping = false
              isSending.value = false
              // 最终渲染
              renderFinalContent(botMsg)
              stopStreamingRender()
              scrollToBottom()
              return
            }
            else if (data.type === 'error') {
              console.error('流式响应错误:', data.data)
              botMsg.content = '请求出错: ' + (data.data.message || '未知错误')
              botMsg.displayContent = botMsg.content
              botMsg.isTyping = false
              isSending.value = false
              stopStreamingRender()
              return
            }
          } catch (error) {
            console.error('解析JSON失败:', error, jsonStr)
            // 如果不是JSON，直接作为内容处理
            latestContent.value = line + '\n'
          }
        } else {
          // 如果不是 data: 格式，直接作为内容处理
          latestContent.value = line + '\n'
        }
      }
    }
  } catch (error) {
    console.error('流式请求失败:', error)
    botMsg.content = '请求失败，请重试'
    botMsg.displayContent = botMsg.content
    botMsg.isTyping = false
    isSending.value = false
    stopStreamingRender()

    // 回退到普通请求
    console.log('流式请求失败，尝试普通请求')
    sendNormalRequest(message, botMsg)
  }
}

// 普通请求作为回退方案
const sendNormalRequest = async (message, botMsg) => {
  try {
    const response = await axios.post('https://api.itxiaozan.com/api/chat', {
      question: message,
      session_id: uuid.value
    })

    if (response && response.data && response.data.answer) {
      botMsg.content = response.data.answer
      botMsg.displayContent = response.data.answer
    }
    botMsg.isTyping = false
    isSending.value = false
    stopStreamingRender()
    scrollToBottom()
  } catch (error) {
    console.error('普通请求错误:', error)
    botMsg.content = '请求失败，请重试'
    botMsg.displayContent = botMsg.content
    botMsg.isTyping = false
    isSending.value = false
    stopStreamingRender()
  }
}

// UUID处理
const uuidToNumber = (uuid) => {
  let number = 0
  for (let i = 0; i < uuid.length && i < 6; i++) {
    const hexValue = uuid[i]
    number = number * 16 + (parseInt(hexValue, 16) || 0)
  }
  return number % 1000000
}

// 新会话
const newChat = async () => {
  console.log('开始新会话')

  // 停止任何正在进行的流式渲染
  stopStreamingRender()

  // 清除旧会话记忆
  if (uuid.value) {
    try {
      await axios.post('https://api.itxiaozan.com/api/memory/clear', {
        session_id: uuid.value
      })
    } catch (error) {
      console.error('清除记忆失败:', error)
    }
  }

  // 生成新会话ID
  const newUUID = uuidToNumber(uuidv4())
  localStorage.setItem('user_uuid', newUUID)
  uuid.value = newUUID

  // 清空消息列表
  messages.value = []

  // 重新发送欢迎消息
  hello()
}

// 创建新会话
const createNewSession = () => {
  // 尝试从本地存储获取UUID，否则生成新的
  let storedUUID = localStorage.getItem('user_uuid')
  if (!storedUUID) {
    storedUUID = uuidToNumber(uuidv4())
    localStorage.setItem('user_uuid', storedUUID)
  }
  uuid.value = storedUUID

  // 清空消息列表
  messages.value = []

  // 发送欢迎消息
  hello()
}

// 组件卸载时清理定时器和事件监听器
import { onUnmounted } from 'vue'
onUnmounted(() => {
  stopStreamingRender()
  // 清理聊天窗口相关的事件监听器
  window.removeEventListener('message', handleChatMessage)
})

// 处理聊天窗口消息的函数（需要在外部定义以便移除）
const handleChatMessage = (e) => {
  if(e.origin !== 'https://ragflow.itxiaozan.com') return;

  if(e.data.type === 'CREATE_CHAT_WINDOW'){
    if(document.getElementById('chat-win')) return;
    const iframe = document.createElement('iframe');
    iframe.id = 'chat-win';
    iframe.src = e.data.src;
    iframe.style.cssText = 'position:fixed;bottom:104px;right:24px;width:380px;height:500px;border:none;background:transparent;z-index:9998;display:none';
    iframe.frameBorder = '0';
    iframe.allow = 'microphone;camera';
    document.body.appendChild(iframe);
  } else if(e.data.type === 'TOGGLE_CHAT'){
    const chatWindow = document.getElementById('chat-win');
    if(chatWindow) chatWindow.style.display = e.data.isOpen ? 'block' : 'none';
  } else if(e.data.type === 'SCROLL_PASSTHROUGH') {
    window.scrollBy(0, e.data.deltaY);
  }
}
</script>

<style scoped>
.app-layout {
  display: flex;
  height: 100vh;
}

.sidebar {
  width: 180px;
  background-color: #1E2030;
  padding: 20px 15px;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #E0E0E0;
  box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
  transition: width 0.3s ease;
}

.logo-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 30px;
}

.logo-text {
  font-size: 20px;
  font-weight: bold;
  margin-top: 15px;
  color: #ffffff;
  text-align: center;
}

.new-chat-button {
  width: 100%;
  margin-top: auto;
  background-color: #23C89F;
  border: none;
  color: white;
  height: 44px;
  font-size: 16px;
}

.new-chat-button:hover {
  background-color: #20B592;
  border: none;
}

.main-content {
  flex: 1;
  padding: 0;
  overflow-y: hidden;
  background-color: #f5f7fa;
  display: flex;
  flex-direction: column;
}

.chat-header {
  padding: 15px 25px;
  background-color: #ffffff;
  border-bottom: 1px solid #e6e6e6;
}

.chat-header h2 {
  margin: 0;
  font-size: 1.5rem;
  color: #303133;
}

.chat-description {
  color: #909399;
  font-size: 0.9rem;
}

.chat-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.message-list {
  flex: 1;
  padding: 20px 25px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #ccc #f1f1f1;
}

.message {
  display: flex;
  margin-bottom: 20px;
  width: 100%;
  align-items: flex-start;
}

.message-icon {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  margin-right: 15px;
  flex-shrink: 0;
}

.message-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.message-user {
  align-self: flex-end;
}

.message-body {
  padding: 12px 18px;
  border-radius: 8px;
  line-height: 1.6;
  font-size: 15px;
  position: relative;
  word-wrap: break-word;
  overflow-wrap: break-word;
  width: fit-content;
  max-width: 85%;
  box-sizing: border-box;
  word-break: break-word;
}

/* 用户消息 */
.user-message {
  align-self: flex-end;
  flex-direction: row-reverse;
}

.user-message .message-icon {
  margin-right: 0;
  margin-left: 15px;
  background-color: #E8F4FF;
  color: #409EFF;
}

.user-message .message-body {
  background-color: #EBF5FF;
  color: #333;
  border: 1px solid #C8E1FF;
}

/* 机器人消息 */
.bot-message {
  align-self: flex-start;
}

.bot-message .message-icon {
  background-color: #F5F7FA;
  color: #606266;
}

.bot-message .message-body {
  background-color: #FFFFFF;
  border: 1px solid #EBEEF5;
}

.loading-dots {
  display: inline-block;
  margin-left: 5px;
}

.loading-dots .dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  background-color: #999;
  border-radius: 50%;
  animation: loading-blink 1.4s infinite both;
  margin: 0 2px;
}

.loading-dots .dot:nth-child(2) {
  animation-delay: 0.2s;
}

.loading-dots .dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes loading-blink {
  0%, 80%, 100% {
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
}

.input-container {
  padding: 15px 25px;
  background-color: #ffffff;
  border-top: 1px solid #e6e6e6;
  display: flex;
  align-items: center;
}

.input-container .el-input {
  flex: 1;
  margin-right: 15px;
}

.input-container .el-input .el-input__inner {
  height: 44px;
  line-height: 44px;
}

.input-container .el-button {
  height: 44px;
  font-size: 16px;
  background-color: #409EFF;
}

.input-container .el-button:hover {
  background-color: #66B1FF;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .main-content {
    padding: 10px 0 10px 0;
  }

  .app-layout {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
  }

  .logo-section {
    flex-direction: row;
    align-items: center;
  }

  .logo-text {
    font-size: 20px;
    margin-top: 0;
    margin-left: 10px;
  }

  .logo-section img {
    width: 40px;
    height: 40px;
  }

  .new-chat-button {
    margin-right: 30px;
    width: auto;
    margin-top: 5px;
  }

  .message-body {
    max-width: 95%;
  }
}

@media (min-width: 769px) {
  .main-content {
    padding: 0;
  }
}
</style>

<style>
/* Markdown 内容样式 */
.markdown-content {
  line-height: 1.6;
  font-size: 15px;
}

/* 标题样式 */
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
  margin: 0.8em 0 0.4em 0;
  font-weight: 600;
  color: #24292e;
  line-height: 1.25;
}

.markdown-content h1 {
  font-size: 1.5em;
  border-bottom: 1px solid #eaecef;
  padding-bottom: 0.3em;
}

.markdown-content h2 {
  font-size: 1.3em;
  border-bottom: 1px solid #eaecef;
  padding-bottom: 0.3em;
}

.markdown-content h3 { font-size: 1.1em; }
.markdown-content h4 { font-size: 1em; }
.markdown-content h5 { font-size: 0.9em; }
.markdown-content h6 { font-size: 0.8em; color: #586069; }

/* 段落和文本 */
.markdown-content p {
  margin: 0.8em 0;
}

.markdown-content strong {
  font-weight: 600;
  color: #24292e;
}

.markdown-content em {
  font-style: italic;
  color: #586069;
}

/* 列表 */
.markdown-content ul,
.markdown-content ol {
  padding-left: 1.8em;
  margin: 0.8em 0;
  color: #24292e;
}

.markdown-content li {
  margin: 0.4em 0;
  line-height: 1.6;
}

/* 引用 */
.markdown-content blockquote {
  border-left: 4px solid #dfe2e5;
  padding-left: 1em;
  margin: 1em 0;
  color: #6a737d;
  background-color: #f6f8fa;
  padding: 0.8em 1em;
  border-radius: 4px;
}

/* 代码 */
.markdown-content code {
  background-color: #f6f8fa;
  padding: 2px 6px;
  border-radius: 4px;
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  font-size: 0.9em;
  border: 1px solid #e1e4e8;
  color: #e74c3c;
}

.markdown-content pre {
  background-color: #f6f8fa;
  border: 1px solid #e1e4e8;
  border-radius: 6px;
  padding: 12px;
  overflow: auto;
  margin: 1em 0;
  font-size: 0.85em;
}

.markdown-content pre code {
  background: none;
  border: none;
  padding: 0;
  color: inherit;
  font-size: 1em;
}

/* 表格 */
.markdown-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 15px 0;
  font-size: 14px;
  background-color: white;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  border: 1px solid #e1e4e8;
  table-layout: auto;
}

.markdown-content th,
.markdown-content td {
  border: 1px solid #e1e4e8;
  padding: 8px 12px;
  text-align: left;
  vertical-align: top;
  word-break: break-word;
}

.markdown-content th {
  background-color: #f6f8fa;
  font-weight: 600;
  color: #24292e;
  border-bottom: 2px solid #e1e4e8;
}

.markdown-content tr:nth-child(even) {
  background-color: #fafbfc;
}

.markdown-content tr:hover {
  background-color: #f6f8fa;
}

/* 水平线 */
.markdown-content hr {
  border: 0;
  height: 2px;
  background-color: #e1e4e8;
  margin: 1.5em 0;
}

/* 链接 */
.markdown-content a {
  color: #0366d6;
  text-decoration: none;
}

.markdown-content a:hover {
  text-decoration: underline;
}

/* 特殊处理用户消息中的内容 */
.user-message .plain-text {
  font-family: inherit;
  white-space: pre-wrap;
}

.user-message .message-body code {
  background-color: #e3f2fd;
  border-color: #bbdefb;
  color: #1976d2;
}

/* 流式输出动画 */
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.markdown-content > * {
  animation: fadeIn 0.3s ease-in;
}

/* Highlight.js 样式优化 */
.hljs {
  background: #f6f8fa !important;
  padding: 1em !important;
  border-radius: 6px;
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  font-size: 0.85em;
  line-height: 1.4;
}

/* 打字机效果 */
.typing-cursor {
  display: inline-block;
  width: 2px;
  height: 1em;
  background-color: #333;
  margin-left: 2px;
  animation: blink 1s infinite;
  vertical-align: middle;
}

@keyframes blink {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}
</style>